# 绘图
import tkinter as tk
from tkinter import simpledialog,messagebox
from PIL import Image, ImageDraw, ImageTk,ImageFont
import random

# windows 字体库
win = {"宋体":"C:/Windows/Fonts/simsun.ttc",
       "黑体":"C:/Windows/Fonts/simhei.ttf",
       "隶书":"C:/Windows/Fonts/SIMLI.TTF"}
# 汉字特征库（模型
mode = {}
fonts = []
# 开始计算
def go():
    global win,mode,fonts
    cn = cn_text.get().split()
    # 转字体列表
    font_all = list(win.keys())
    fonts = []
    for i, var in enumerate(font_vars):
        if var.get():
            # 当前选中的字体
            font = font_all[i]
            fonts.append(font)

    # 显示特征库
    img_mode = str_font_size(cn,fonts)
    # print(img_mode)
    mode = ml(img_mode)
    # print(mode)
    test()

# 计算概率
def ml(old_mode):
    font_size = int(size_var.get())
    new_mode = {}
    for m in old_mode:
        # print("字",m)
        size_arr = []
        #  遍历相同字号的不同字体
        for font in old_mode[m]:
            i = 0
            # print("字体",font)
            for size in old_mode[m][font]:
                # print("大小",size)
                ps = old_mode[m][font][size]
                # 返回特征当前图片的图案特征（3×3卷积核）
                rect_ps = mode_rect({'data': ps},size, 3)
                # print("图案",rect_ps)
                # 列表是否存在
                try:
                    size_arr[i].append(rect_ps['data'])
                except IndexError:
                    size_arr.append([rect_ps['data']])
                i += 1
        # 开始遍历相同字号的不同字体
        size_mode = {}
        iu = 0
        for num in size_arr:
            # 图案特征
            yx_mode = {}
            # 返回第一个数组的坐标
            yx_keys = num[0].keys()
            for k in yx_keys:
                # 初始化这个点
                yx_mode[k] = {}
            # 开始遍历
            for ii in num:
                for yx_name in ii:
                    rect_str = ii[yx_name].values()
                    result  = "".join(str(item) for item in rect_str)
                    # 词典的键是否存在
                    if result in yx_mode[yx_name]:
                        yx_mode[yx_name][result] += 1
                    else:
                        yx_mode[yx_name][result] = 1
                    # print(yx_mode)
            # 统计不同大小的汉字特征图案出现数量
            size_mode[font_size - iu] = yx_mode
            iu += 2
        new_mode[m] = size_mode
    # print(new_mode)
    return new_mode

# 测试
def test():
    # 重复训练的过程
    global mode,fonts
    # 返回用户输入的汉字
    test_str = simpledialog.askstring('输入汉字', '请输入一个汉字用于测试！')
    if not test_str:
        return
    # 字体大小
    font_size = int(size_var.get())
    font_len = len(fonts)
    # 随机选一种字体
    font_name = fonts[int(random.random() * font_len)]
    # print(font_size,font_name,test_str)
    # 绘制单一文字，单一字体
    str_mode = str_font_size([test_str], [font_name])
    # print(str_mode)
    str_ml = ml(str_mode)
    # print(str_ml)
    max = 0
    word = ''
    for key in mode:
        str_len = 0
        str_weight = 0
        for size in mode[key]:
            str_len += 1
            size_len = 0
            size_weight = 0
            for yx in mode[key][size]:
                size_len += 1
                # 返回名字（图案值）
                yx_names = str_ml[test_str][size][yx].keys()
                rect_str = list(yx_names)[0]
                # 输出图案值
                print(rect_str)
                yx_len = 0
                yx_weight = 0
                # 开始遍历
                for rect_key in mode[key][size][yx]:
                    yx_len += 1
                    # 如果图案值相等
                    if (rect_key == rect_str):
                        # 如果命中就累积权重，字体越大权重越高，这点很重要
                        yx_weight += str_len * mode[key][size][yx][rect_key] / font_len
                # 累加命中率
                size_weight += yx_weight / yx_len
            str_weight += size_weight / size_len
        # 计算总的命中率（命中率：命中数总权重 /特征位总权重）
        ps_i = str_weight / str_len
        # print(key, ps_i)
        if (ps_i > max):
            max = ps_i
            word = key
    # 显示结果
    messagebox.showinfo('识别结果', f'你输入的这个字很可能是：{word}')

# 生成不同字体不同大小的图片，并返回各个像素点的坐标值  
def str_font_size(cn, fonts):
    global win
    size = int(size_var.get())
    img_mode = {}
    # 遍历汉字
    for k in cn:
        # 图像显示区域
        frame = tk.Frame(root)
        frame.pack()
        # 这个字的特征库
        font_mode = {}
        # 遍历字体
        for font in fonts:
            # 字库路径
            font_path = win[font]
            # 生成原图
            cs = Image.new('RGBA', (size, size), (255, 255, 255, 0))
            draw = ImageDraw.Draw(cs)
            # 设置字体和大小
            font_str = ImageFont.truetype(font_path, size)
            draw.text((0,0), k, font=font_str, fill=(0, 0, 0, 255))
            photo = ImageTk.PhotoImage(cs)
            label = tk.Label(frame, image=photo)
            label.image = photo
            label.pack(side='left')
            # 缩进，步长为-2像素
            font_size = {}
            for j in range(size, 0, -2):
                # 绘制并显示图像
                cs_zoom = cs.resize((j, j))
                photo_zoom = ImageTk.PhotoImage(cs_zoom)
                label = tk.Label(frame, image=photo_zoom)
                label.image = photo_zoom
                label.pack(side='left')
                # 返回缩放后图片的数据集
                data = list(cs_zoom.getdata())
                point = {}
                # 逐行
                for ii in range(j):
                    # 逐列
                    for jj in range(j):
                        id = f"{ii}_{jj}"
                        ss = ii *j  + jj
                        # 这里使用透明度即可
                        if data[ss][3] > 0:
                            point[id] = 1
                        else:
                            point[id] = 0
                # 追加到指定尺寸的数组中
                font_size[j] = point
            # 更新这个字的特征库
            font_mode[font] = font_size
        # 更新这个字体的所有文字的特征库
        img_mode[k] = font_mode
    # 返回所有图片文字的坐标值
    return img_mode

# 图案特征库，图案大小：默认边长为3的正方形
def mode_rect(old_mode,w, rect=3):
    new_mode = {}
    for key in old_mode:
        new_mode[key] = {}
        ps = old_mode[key]
        # 逐行逐列
        for p in ps:
            if (ps[p] == 1 or ps[p] == 0):
                new_mode[key][p] = {}
                yx = p.split('_')
                yx_y = int(yx[0])
                yx_x = int(yx[1])
                y_start = yx_y - (rect - 1) // 2
                if y_start < 0:
                    y_start = 0
                y_end = y_start + rect
                if y_end > w:
                    y_end = w
                x_start = yx_x - (rect - 1) // 2
                if x_start < 0:
                    x_start = 0
                x_end = x_start + rect
                if x_end > w:
                    x_end = w
                # 开始在区域内遍历像素点
                for y_i in range(y_start, y_end):
                    for x_i in range(x_start, x_end):
                        rect_yx = f'{y_i}_{x_i}'
                        if (ps[rect_yx] == 1):
                            new_mode[key][p][rect_yx] = 1
                        else:
                            new_mode[key][p][rect_yx] = 0
    return new_mode

# 绘制窗体
root = tk.Tk()
root.title("多字体汉字识别")
size_var = tk.StringVar()
size_var.set("16")
# 输入框
cn_text = tk.Entry(root)
cn_text.insert(0,'人 口 手 金 木 水 火 土 阴 阳')
cn_text.pack()
# 字体集合
font_vars = []
# 字体多选框
frame_font = tk.Frame(root)
frame_font.pack()
for font in win:
    var = tk.BooleanVar()
    var.set(True)
    font_vars.append(var)
    checkbutton = tk.Checkbutton(frame_font, text=font, variable=var)
    checkbutton.pack(side='left')

# 按钮
frame = tk.Frame(root)
frame.pack()
tk.Button(frame, text='测试', command=test).pack(side='left')
# 像素大小输入框
tk.Label(frame, text='像素大小：').pack(side='left')
tk.Entry(frame,textvariable=size_var).pack(side='left')
tk.Button(frame, text='开始', command=go).pack(side='left')

# 图片显示区域

root.mainloop()